<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  <meta name="description" content="along&#39;s blog">
  <meta name="keyword" content="hexo-theme, vuejs">
  
    <link rel="shortcut icon" href="/css/images/logo.png">
  
  <title>
    
      vue响应式原理 | ALONG
    
  </title>
  <link href="/cdn.along/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <link href="/cdn.along/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">
  <link href="/cdn.along/highlight.js/9.12.0/styles/tomorrow.min.css" rel="stylesheet">
  
<link rel="stylesheet" href="/css/style.css">

  
  <script src="/cdn.along/jquery/3.2.1/jquery.min.js"></script>
  <script src="/cdn.along/geopattern/1.2.3/js/geopattern.min.js"></script>
  <script src="/cdn.along/nprogress/0.2.0/nprogress.min.js"></script>
  
    
<script src="/js/qrious.js"></script>

  
  
  
  
    <!-- MathJax support START -->
    <script type="text/x-mathjax-config">
      MathJax.Hub.Config({
        tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
        }
      });
    </script>

    <script type="text/x-mathjax-config">
      MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax(), i;
        for (i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
        }
      });
    </script>
    <script type="text/javascript" src="/cdn.along/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
    <!-- MathJax support END -->
  


  
  
    
<script src="/js/local-search.js"></script>


<meta name="generator" content="Hexo 5.4.0"></head>
<div class="wechat-share">
  <img src="/css/images/logo.png" />
</div>
  <body>
    <header class="header fixed-header">
  <div class="header-container">
    <a class="home-link" href="/">
      <div class="logo"></div>
      <span>ALONG</span>
    </a>
    <ul class="right-list">
      
        <li class="list-item">
          
            <a href="/" class="item-link">Home</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/tags/" class="item-link">Tags</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/archives/" class="item-link">Archives</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/about/" class="item-link">About</a>
          
        </li>
      
      
        <li class="menu-item menu-item-search right-list">
    <a role="button" class="popup-trigger">
        <i class="fa fa-search fa-fw"></i>
    </a>
</li>
      
    </ul>
    <div class="menu">
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
    </div>
    <div class="menu-mask">
      <ul class="menu-list">
        
          <li class="menu-item">
            
              <a href="/" class="menu-link">Home</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/tags/" class="menu-link">Tags</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/archives/" class="menu-link">Archives</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/about/" class="menu-link">About</a>
            
          </li>
        
      </ul>
    </div>
    
      <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
            <span class="search-icon">
                <i class="fa fa-search"></i>
            </span>
            <div class="search-input-container">
                <input autocomplete="off" autocapitalize="off"
                    placeholder="Please enter your keyword(s) to search." spellcheck="false"
                    type="search" class="search-input">
            </div>
            <span class="popup-btn-close">
                <i class="fa fa-times-circle"></i>
            </span>
        </div>
        <div id="search-result">
            <div id="no-result">
                <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
            </div>
        </div>
    </div>
</div>
    
  </div>
</header>

    <div id="article-banner">
  <h2>vue响应式原理</h2>
  <p class="post-date">2020-08-27</p>
  <div class="arrow-down">
    <a href="javascript:;"></a>
  </div>
</div>
<main class="app-body flex-box">
  <!-- Article START -->
  <article class="post-article">
    <section class="markdown-content"><h2 id="VUE-响应式原理"><a href="#VUE-响应式原理" class="headerlink" title="VUE 响应式原理"></a>VUE 响应式原理</h2><h3 id="数据响应式原理"><a href="#数据响应式原理" class="headerlink" title="数据响应式原理"></a>数据响应式原理</h3><p>Vue 2.x</p>
<blockquote>
<p>当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项，Vue 将遍历此对象所有的 property，并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性，这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。</p>
</blockquote>
<p>defineProperty 代码示例，详见 <a href="./test/defineproperty.html">defineProty示例</a></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> data = &#123;</span><br><span class="line">    msg: <span class="string">&#x27;hello&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 数据劫持</span></span><br><span class="line"><span class="built_in">Object</span>.defineProperty(data, <span class="string">&#x27;msg&#x27;</span>, &#123;</span><br><span class="line">    <span class="comment">// 可枚举</span></span><br><span class="line">    enumerable: <span class="literal">true</span>,</span><br><span class="line">    <span class="comment">// 可配置(可以使用delete删除)</span></span><br><span class="line">    configurable: <span class="literal">true</span>,</span><br><span class="line"></span><br><span class="line">    get() &#123;</span><br><span class="line">        <span class="comment">// 需要用一个新的字段保存实际的msg值，因为在get中如果访问msg会导致重复触发getter</span></span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;get:&#x27;</span>, data._msg);</span><br><span class="line">        <span class="keyword">return</span> data._msg;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    set(newValue) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;set:&#x27;</span>, newValue);</span><br><span class="line">        <span class="keyword">if</span> (newValue === data._msg) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        data._msg = newValue;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">document</span>.querySelector(<span class="string">&#x27;#app&#x27;</span>).textContent = data._msg;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 测试</span></span><br><span class="line">data.msg = <span class="string">&#x27;hello world&#x27;</span>;</span><br><span class="line"><span class="built_in">console</span>.log(data.msg);</span><br></pre></td></tr></table></figure>

<p>Vue 3.x</p>
<p>Proxy 代码示例， 详见 <a href="./test/proxy.html">Proxy示例</a></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> data = &#123;</span><br><span class="line">    msg: <span class="string">&#x27;hello&#x27;</span>,</span><br><span class="line">    count: <span class="number">0</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> vm = <span class="keyword">new</span> <span class="built_in">Proxy</span>(data, &#123;</span><br><span class="line">    get (target, key) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;get, key: &#x27;</span>, key, target[key]);</span><br><span class="line">        <span class="keyword">return</span> target[key];</span><br><span class="line">    &#125;,</span><br><span class="line">    set(target, key, newValue) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;set, key:&#x27;</span>, key, newValue);</span><br><span class="line">        <span class="keyword">if</span> (target[key] === newValue) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        target[key] = newValue;</span><br><span class="line">        <span class="built_in">document</span>.querySelector(<span class="string">&#x27;#app&#x27;</span>).textContent = target[key];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// test</span></span><br><span class="line">vm.msg = <span class="string">&#x27;along&#x27;</span>;</span><br><span class="line"></span><br><span class="line">vm.count = <span class="number">20</span>;</span><br></pre></td></tr></table></figure>

<h3 id="发布订阅模式-与-观察者模式"><a href="#发布订阅模式-与-观察者模式" class="headerlink" title="发布订阅模式 与 观察者模式"></a>发布订阅模式 与 观察者模式</h3><p>发布订阅模式</p>
<p>vue和node事件基于发布订阅模式</p>
<p>vue 自定义事件示例,实现兄弟组件间的通讯</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// eventBus.js 事件中心</span></span><br><span class="line"><span class="keyword">let</span> eventBus = <span class="keyword">new</span> Vue();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 注册两个事件，A、B 在created生命周期中注册两个订阅者，C 发布事件</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// A,vue </span></span><br><span class="line">created() &#123;</span><br><span class="line">    eventBus.$on(<span class="string">&#x27;datachange&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;datachange, then 1&#x27;</span>);</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// B.vue</span></span><br><span class="line">created() &#123;</span><br><span class="line">    eventBus.$on(<span class="string">&#x27;datachange&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;datachange, then 2&#x27;</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// C.vue</span></span><br><span class="line"><span class="comment">// 触发事件，发布者</span></span><br><span class="line">methods: &#123;</span><br><span class="line">    onDataChange() &#123;</span><br><span class="line">        eventBus.$emit(<span class="string">&#x27;datachange&#x27;</span>, &#123;<span class="attr">text</span>: <span class="string">&#x27;along&#x27;</span>&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>模拟实现 事件中心 详见 <a href="./test/eventemitter.html">eventemitter</a></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 事件触发器</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">EventEmitter</span> </span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>() &#123;</span><br><span class="line">        <span class="built_in">this</span>.subs = <span class="built_in">Object</span>.create(<span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    $on(eventType, handler) &#123;</span><br><span class="line">        <span class="comment">// 如果没有注册过该事件，设置该事件为空数组</span></span><br><span class="line">        <span class="built_in">this</span>.subs[eventType] = <span class="built_in">this</span>.subs[eventType] || [];</span><br><span class="line">        <span class="built_in">this</span>.subs[eventType].push(handler);</span><br><span class="line">    &#125;   </span><br><span class="line"></span><br><span class="line">    $emit(eventType) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.subs[eventType]) &#123;</span><br><span class="line">            <span class="built_in">this</span>.subs[eventType].forEach(<span class="function"><span class="params">handler</span> =&gt;</span> &#123;</span><br><span class="line">                handler(); </span><br><span class="line">            &#125;)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// test</span></span><br><span class="line"><span class="keyword">let</span> em = <span class="keyword">new</span> EventEmitter();</span><br><span class="line"></span><br><span class="line">em.$on(<span class="string">&#x27;click&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;click then 1&#x27;</span>);</span><br><span class="line">&#125;)</span><br><span class="line">em.$on(<span class="string">&#x27;click&#x27;</span>, <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">&#x27;click then 2&#x27;</span>);</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">em.$emit(<span class="string">&#x27;click&#x27;</span>);</span><br></pre></td></tr></table></figure>

<p>观察者模式</p>
<p>vue响应式机制使用了观察者模式</p>
<p>观察者模式没有事件中心，只有发布者和订阅者，发布者需要知道订阅者的存在</p>
<ul>
<li>观察者模式中，订阅者称为观察者，Watcher<ul>
<li>update(), 观察者需要实现update方法，定义事件发生时具体要做的事情</li>
</ul>
</li>
<li>发布者，Dep<ul>
<li>subs: 存储观察者的数组</li>
<li>addSub(): 注册添加观察者</li>
<li>notify(): 事件发生时，通知所有的观察者update()方法</li>
</ul>
</li>
</ul>
<p>观察者模式代码示例，详见<a href="./test/depwatcher.html"></a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; 发布者</span><br><span class="line">class Dep &#123;</span><br><span class="line">    constructor() &#123;</span><br><span class="line">        &#x2F;&#x2F; 记录所有的订阅者</span><br><span class="line">        this.subs &#x3D; [];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    addSub(sub) &#123;</span><br><span class="line">        if (sub &amp;&amp; sub.update) &#123;</span><br><span class="line">            this.subs.push(sub);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    notify() &#123;</span><br><span class="line">        this.subs.forEach(sub &#x3D;&gt; &#123;</span><br><span class="line">            sub.update();</span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; 订阅者</span><br><span class="line">class Watcher &#123;</span><br><span class="line">    update() &#123;</span><br><span class="line">        console.log(&#39;update&#39;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; test</span><br><span class="line">let dep &#x3D; new Dep();</span><br><span class="line">let watcher &#x3D; new Watcher();</span><br><span class="line"></span><br><span class="line">dep.addSub(watcher);</span><br><span class="line"></span><br><span class="line">dep.notify();</span><br></pre></td></tr></table></figure>

<p>观察者模式和发布订阅模式的区别</p>
<ul>
<li>观察者模式由具体目标调度，当时间触发，Dep就会调用观察者的方法，观察者和发布者之间存在依赖关系</li>
<li>发布/订阅模式由事件中心调度，发布者和订阅者不需要知道对方的存在</li>
</ul>
<p><img src="./readmeimg/diffofwatcher.png"></p>
<h3 id="vue-响应式原理总览"><a href="#vue-响应式原理总览" class="headerlink" title="vue 响应式原理总览"></a>vue 响应式原理总览</h3><p><img src="./readmeimg/vue.png"></p>
<h4 id="VUE"><a href="#VUE" class="headerlink" title="VUE"></a>VUE</h4><ul>
<li>负责接收初始化参数</li>
<li>负责把data中的属性注入到Vue中，转换为getter/setter</li>
<li>负责调用Observer监听data中属性的变换</li>
<li>负责调用Compiler解析指令/差值表达式</li>
</ul>
<p>类图<br>|  VUE   |<br>|  —-  |<br>| + $options <br/> + $el <br/> + $data|<br>| - _proxyDate() | </p>
<h4 id="Observer-数据劫持"><a href="#Observer-数据劫持" class="headerlink" title="Observer 数据劫持"></a>Observer 数据劫持</h4><ul>
<li>负责把data中的数据转换成响应式数据</li>
<li>data中的某个属性是对象时，把该对象属性也转换成响应式数据</li>
<li>数据变化时发送通知</li>
</ul>
<p>类图<br>|  Observer   |<br>|  —-  |<br>| + walk() <br/> + defineReactive|</p>
<h4 id="Compiler"><a href="#Compiler" class="headerlink" title="Compiler"></a>Compiler</h4><ul>
<li>编译模板，解析指令差值表达式</li>
<li>负责页面的首次渲染</li>
<li>数据变化时更新视图</li>
</ul>
<p>类图<br>|  Compiler   |<br>|  —-  |<br>| + el <br/> + vm|<br>| + compile(el) <br/> + compilerElement(node) <br/> + compileText(node) <br/> + isDirective(attrName) <br> + isTextNode(node) <br/> + isElementNode(node) |</p>
<ul>
<li>compile(el) 解析节点，如果是文本节点，解析差值表达式，如果是元素节点解析指令</li>
<li>compileElement(node) 解析元素节点</li>
<li>compileText(node) 解析文本节点</li>
<li>isDirective(attrName) 判断属性是否是指令</li>
</ul>
<h4 id="Dep-Dependency"><a href="#Dep-Dependency" class="headerlink" title="Dep(Dependency)"></a>Dep(Dependency)</h4><ul>
<li>收集依赖，添加观察者</li>
<li>通知所有观察者更新数据</li>
</ul>
<p>类图<br>|  Dep   |<br>|  —-  |<br>| + subs |<br>| + addSub(sub) <br/> + notify() | </p>
<h4 id="Watcher"><a href="#Watcher" class="headerlink" title="Watcher"></a>Watcher</h4><ul>
<li>数据变化时dep通知所有watcher 更新视图</li>
<li>自身实例化时 注册到dep 对象中</li>
</ul>
<p>类图<br>|  Watcher   |<br>|  —-  |<br>| + vm <br/> + key <br/> + cb <br/> + oldValue |<br>| + update() | </p>
<h3 id="源码实现"><a href="#源码实现" class="headerlink" title="源码实现"></a>源码实现</h3><p>了解了响应式原理的几个部分，本项目简易实现一个响应式vue</p>
<p>目前实现的功能</p>
<ul>
<li><input disabled="" type="checkbox"> 差值表达式 <ul>
<li><input checked="" disabled="" type="checkbox"> 绑定值 <code>&#123;&#123;data&#125;&#125;</code></li>
<li><input disabled="" type="checkbox"> 表达式 <code>&#123;&#123;data + 1&#125;&#125;</code></li>
<li><input disabled="" type="checkbox"> …</li>
</ul>
</li>
<li><input disabled="" type="checkbox"> 模板指令<ul>
<li><input checked="" disabled="" type="checkbox"> <code>v-text</code></li>
<li><input checked="" disabled="" type="checkbox"> <code>v-html</code></li>
<li><input checked="" disabled="" type="checkbox"> <code>v-model</code> 双向绑定</li>
<li><input disabled="" type="checkbox"> <code>v-on</code><ul>
<li><input checked="" disabled="" type="checkbox"> 绑定<code>vue</code>中的<code>methods</code> </li>
<li><input disabled="" type="checkbox"> …</li>
</ul>
</li>
</ul>
</li>
</ul>
</section>
    <!-- Tags START -->
    
      <div class="tags">
        <span>Tags:</span>
        
  <a href="/tags#vue" >
    <span class="tag-code">vue</span>
  </a>

      </div>
    
    <!-- Tags END -->
    <!-- NAV START -->
    
  <div class="nav-container">
    <!-- reverse left and right to put prev and next in a more logic postition -->
    
      <a class="nav-left" href="/2020/08/27/vue-router%E5%8E%9F%E7%90%86/">
        <span class="nav-arrow">← </span>
        
          vue-router原理
        
      </a>
    
    
      <a class="nav-right" href="/2020/08/27/%E8%99%9A%E6%8B%9Fdom/">
        
          vue虚拟dom
        
        <span class="nav-arrow"> →</span>
      </a>
    
  </div>

    <!-- NAV END -->
    <!-- 打赏 START -->
    
      <div class="money-like">
        <div class="reward-btn">
          赏
          <span class="money-code">
            <span class="alipay-code">
              <div class="code-image"></div>
              <b>使用支付宝打赏</b>
            </span>
            <span class="wechat-code">
              <div class="code-image"></div>
              <b>使用微信打赏</b>
            </span>
          </span>
        </div>
        <p class="notice">若你觉得我的文章对你有帮助，欢迎点击上方按钮对我打赏</p>
      </div>
    
    <!-- 打赏 END -->
    <!-- 二维码 START -->
    
      <div class="qrcode">
        <canvas id="share-qrcode"></canvas>
        <p class="notice">扫描二维码，分享此文章</p>
      </div>
    
    <!-- 二维码 END -->
    
      <!-- No Comment -->
    
  </article>
  <!-- Article END -->
  <!-- Catalog START -->
  
    <aside class="catalog-container">
  <div class="toc-main">
    <strong class="toc-title">Catalog</strong>
    
      <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#VUE-%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86"><span class="toc-nav-text">VUE 响应式原理</span></a><ol class="toc-nav-child"><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#%E6%95%B0%E6%8D%AE%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86"><span class="toc-nav-text">数据响应式原理</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%A8%A1%E5%BC%8F-%E4%B8%8E-%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F"><span class="toc-nav-text">发布订阅模式 与 观察者模式</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#vue-%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86%E6%80%BB%E8%A7%88"><span class="toc-nav-text">vue 响应式原理总览</span></a><ol class="toc-nav-child"><li class="toc-nav-item toc-nav-level-4"><a class="toc-nav-link" href="#VUE"><span class="toc-nav-text">VUE</span></a></li><li class="toc-nav-item toc-nav-level-4"><a class="toc-nav-link" href="#Observer-%E6%95%B0%E6%8D%AE%E5%8A%AB%E6%8C%81"><span class="toc-nav-text">Observer 数据劫持</span></a></li><li class="toc-nav-item toc-nav-level-4"><a class="toc-nav-link" href="#Compiler"><span class="toc-nav-text">Compiler</span></a></li><li class="toc-nav-item toc-nav-level-4"><a class="toc-nav-link" href="#Dep-Dependency"><span class="toc-nav-text">Dep(Dependency)</span></a></li><li class="toc-nav-item toc-nav-level-4"><a class="toc-nav-link" href="#Watcher"><span class="toc-nav-text">Watcher</span></a></li></ol></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#%E6%BA%90%E7%A0%81%E5%AE%9E%E7%8E%B0"><span class="toc-nav-text">源码实现</span></a></li></ol></li></ol>
    
  </div>
</aside>
  
  <!-- Catalog END -->
</main>

<script>
  (function () {
    var url = 'https://github.com/alongithub/2020/08/27/vue响应式原理/';
    var banner = ''
    if (banner !== '' && banner !== 'undefined' && banner !== 'null') {
      $('#article-banner').css({
        'background-image': 'url(' + banner + ')'
      })
    } else {
      $('#article-banner').geopattern(url)
    }
    $('.header').removeClass('fixed-header')

    // error image
    $(".markdown-content img").on('error', function() {
      $(this).attr('src', '/css/images/error_icon.png')
      $(this).css({
        'cursor': 'default'
      })
    })

    // zoom image
    $(".markdown-content img").on('click', function() {
      var src = $(this).attr('src')
      if (src !== '/css/images/error_icon.png') {
        var imageW = $(this).width()
        var imageH = $(this).height()

        var zoom = ($(window).width() * 0.95 / imageW).toFixed(2)
        zoom = zoom < 1 ? 1 : zoom
        zoom = zoom > 2 ? 2 : zoom
        var transY = (($(window).height() - imageH) / 2).toFixed(2)

        $('body').append('<div class="image-view-wrap"><div class="image-view-inner"><img src="'+ src +'" /></div></div>')
        $('.image-view-wrap').addClass('wrap-active')
        $('.image-view-wrap img').css({
          'width': `${imageW}`,
          'transform': `translate3d(0, ${transY}px, 0) scale3d(${zoom}, ${zoom}, 1)`
        })
        $('html').css('overflow', 'hidden')

        $('.image-view-wrap').on('click', function() {
          $(this).remove()
          $('html').attr('style', '')
        })
      }
    })
  })();
</script>


  <script>
    var qr = new QRious({
      element: document.getElementById('share-qrcode'),
      value: document.location.href
    });
  </script>






    <div class="scroll-top">
  <span class="arrow-icon"></span>
</div>
    <footer class="app-footer">
  <p class="copyright">
    Copyright&copy; 2019-2022 along
    <br>
    <a href="https://beian.miit.gov.cn/" target="_blank">京ICP备2020044791号-1</a>
    <!-- <br>
    Theme by <a href="https://github.com/alongithub">alongithub</a>
     -->
  </p>
</footer>

<script>
  function async(u, c) {
    var d = document, t = 'script',
      o = d.createElement(t),
      s = d.getElementsByTagName(t)[0];
    o.src = u;
    if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
    s.parentNode.insertBefore(o, s);
  }
</script>
<script>
  async("/cdn.along/fastclick/1.0.6/fastclick.min.js", function(){
    FastClick.attach(document.body);
  })
</script>

<script>
  var hasLine = 'true';
  async("/cdn.along/highlight.js/9.12.0/highlight.min.js", function(){
    $('figure pre').each(function(i, block) {
      var figure = $(this).parents('figure');
      if (hasLine === 'false') {
        figure.find('.gutter').hide();
      }
      hljs.configure({useBR: true});
      var lang = figure.attr('class').split(' ')[1] || 'code';
      var codeHtml = $(this).html();
      var codeTag = document.createElement('code');
      codeTag.className = lang;
      codeTag.innerHTML = codeHtml;
      $(this).attr('class', '').empty().html(codeTag);
      figure.attr('data-lang', lang.toUpperCase());
      hljs.highlightBlock(block);
    });
  })
</script>
<!-- Baidu Tongji -->


<script src="/js/script.js"></script>


  </body>
</html>